package com.black.utils.network.app.ftp;

import com.black.utils.commons.network.Sender;
import com.black.utils.commons.pool.PoolProperties;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.SocketTimeoutException;

/**
 * @author ylx
 */
public class FTPSender extends Sender<FTPClient> {
    public FTPSender() {
        this("/ftp.properties");
    }

    public FTPSender(String sourcePath) {
        super(new PoolProperties(sourcePath), FTPClientFactory.class);
    }

    public boolean send(String localPath) {
        return send(new File(localPath));
    }

    public boolean send(File file) {
        return send(file, 1);
    }

    /**
     * 尝试发送一个文件到FTP服务器。
     *
     * @param file     要发送的文件对象，不应为null。
     * @param attempts 已经尝试发送的次数。
     * @return 布尔值，如果文件成功发送则返回true，否则返回false。
     */
    public boolean send(File file, int attempts) {
        boolean isSend = false;
        // 构建远程服务器的文件路径
        String name = file.getName();
        String remotePath = properties.getRootPath() + name;
        FTPClient ftpClient = null;
        try (FileInputStream inputStream = new FileInputStream(file)) {
            // 获取FTP客户端
            ftpClient = pool.borrowObject();
            if (null != ftpClient) {
                // 进入被动模式
                ftpClient.enterLocalPassiveMode();
                // 尝试存储文件到远程路径
                isSend = ftpClient.storeFile(remotePath, inputStream);
            }
        } catch (SocketTimeoutException e) {
            log.warn("FTP Client disconnect : {}", e.getMessage());
            // 如果断开连接时远程文件已存在，则视为发送成功
            isSend = checkRemoteFileExist(file, remotePath);
        } catch (IOException e) {
            log.error("FTP upload failed, file: {},exception:{}", name, e.getMessage());
        } catch (Exception e) {
            log.error("No ftpClient is available");
        } finally {
            // 无论成功或失败，都必须归还FTP客户端实例到池中
            pool.returnObject(ftpClient);
        }
        // 如果尝试次数未超过最大次数，则尝试重新发送
        if (!isSend && attempts <= properties.getMaxCount()) {
            log.warn("FTP upload [{}] failed, retrying..., attempts: [{}]", name, attempts);
            // 递归尝试发送文件
            return send(file, attempts + 1);
        }
        // 如果超过最大尝试次数还未成功，则返回false
        return isSend;
    }

    /**
     * 连接超时或其他原因断开时 检测文件是否存在
     *
     * @param filepath 远程文件路径
     * @return 布尔值，如果文件存在则返回true，否则返回false。
     */
    private boolean checkRemoteFileExist(File file, String filepath) {
        FTPClient ftpClient = null;
        try {
            ftpClient = pool.borrowObject();
            if (null == ftpClient) {
                return false;
            }
            FTPFile[] ftpFiles = ftpClient.listFiles(filepath);
            if (ftpFiles.length == 0) {
                log.debug("file not exist");
                return false;
            }
            FTPFile ftpFile = ftpFiles[0];
            if (ftpFile.getSize() != file.length()) {
                log.error("remote file exist but size not equals");
                return false;
            }
            return true;
        } catch (Exception e) {
            log.error("FTP check remote file exist failed, filepath: {},Exception: {}", filepath, e.getMessage());
        } finally {
            pool.returnObject(ftpClient);
        }
        return false;
    }
}
